# @Time : 2024/12/17 22:23
# @Author : Fioman 
# @Phone : 13149920693
"""
数据库的原始操作类,对数据库的底层封装
"""
import threading
from typing import Union, Dict, List

import pymongo

import app_config
from common.produce_logger import debug_produce
from db_tool.db_config import TableName, TableRegister
from personal_tool.data_result import DataResult


class DbHandler(object):
    """
    DbHandler封装的MongoDb接口,包括插入数据,更新数据等.
    """
    url = "mongodb://localhost:27017/"
    dbName = app_config.projectName
    _instance = None
    _initFlag = False
    _lock = threading.Lock()

    def __new__(cls):
        if not cls._instance:
            with cls._lock:
                cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        if not DbHandler._initFlag:
            debug_produce("DbHandler.__init__() 进行初始化")
            DbHandler._initFlag = True
            self.client = pymongo.MongoClient(DbHandler.url)
            self.paramsDb = self.client[self.dbName]
            self.create_table_if_not_exist()

    def create_table_if_not_exist(self):
        """
        动态创建所有已注册的表
        所有的表的注册都在它们自己的文件中去执行,然后执行的方式,是在模块的__init__中去导入
        这样的好处就是后面每次创建表的时候,只需要在table_init()中添加一个文件即可,这里只需要去遍历
        所有的表明和初始化的文件即可.
        :return:
        """
        for tableName, initData in TableRegister.get_all_tables().items():
            self.init_data(tableName, initData)

    def insert_data(self, tableName: Union[str, TableName], data: Union[Dict, List[Dict]]) -> DataResult:
        """
         传入的数据的数据类型可能是列表,如果是列表,列表里面的每一项都是字典
         ,则认为是插入多条记录,否则就认为是插入一条记录
        @param tableName:可能是字符串,也可能是枚举类型(表明通过枚举,这样不用每次都书写字符串)
        @param data:
        @return:
        """
        # 如果是枚举类型,就转换成其属性的字符串
        if isinstance(tableName, TableName):
            tableName = tableName.name
        result = DataResult()
        try:
            if isinstance(data, list) and len(data) > 0 and isinstance(data[0], dict):
                self.paramsDb[tableName].insert_many(data)
                result.state = True
            elif isinstance(data, dict):
                self.paramsDb[tableName].insert_one(data)
                result.state = True
            else:
                result.info = ("in in DbHandler().insert_data() error: 插入的数据类型错误,"
                               "请传入字典或者是字典的列表")
        except Exception as e:
            result.info = str(e)
            debug_produce(f"in DbHandler().insert_data() error: {str(e)}")
            return result
        return result

    def update_data(self, tableName: Union[str, TableName], query: Dict,
                    newValues: Dict, updateOne: bool = False) -> DataResult:
        """
        更新数据,根据query筛选条件去更新数据
        @param tableName:表名
        @param query:筛选条件
        @param newValues:新的值
        @param updateOne:是否只更新一条数据的标志位,默认是False,更新全部
        @return:更新成功返回
        """
        result = DataResult()
        if isinstance(tableName, TableName):
            tableName = tableName.name
        try:
            if updateOne:
                self.paramsDb[tableName].update_one(query, newValues)
            else:
                self.paramsDb[tableName].update_many(query, {'$set': newValues})
            result.state = True
        except Exception as e:
            result.state = str(e)
            debug_produce(f"in DbHandler().update_data() error: {str(e)}")
            return result
        return result

    def delete_data(self, tableName: Union[str, TableName], query: Dict,
                    deleteOne: bool = False) -> DataResult:
        """
        删除数据,根据数据库表明和指定的条件去删除数据记录,可以指定删除一条,还是全部
        @param tableName: 表明
        @param query: 删除条件
        @param deleteOne: 是否只删除一条记录(如果有多条满足条件的记录的话)
        @return:
        """
        result = DataResult()
        if isinstance(tableName, TableName):
            tableName = tableName.name
        try:
            if deleteOne:
                self.paramsDb[tableName].delete_one(query)
            else:
                self.paramsDb[tableName].delete_many(query)
            result.state = True
        except Exception as e:
            result.info = str(e)
            debug_produce(f"in DbHandler().delete_data() error: {str(e)}")
            return result
        return result

    def drop_table(self, tableName: Union[str, TableName]) -> DataResult:
        """
        删除表,丢弃表,不是删除数据
        @param tableName:
        @return:
        """
        result = DataResult()
        if isinstance(tableName, TableName):
            tableName = tableName.name
        try:
            self.paramsDb[tableName].drop()
            result.state = True
        except Exception as e:
            result.info = str(e)
            debug_produce(f"in DbHandler().drop_table() error: {str(e)}")
            return result
        return result

    def get_data_list(self, tableName: Union[str, TableName], query: Dict = None,
                      projection: Dict = None, sortName: str = "", isReverse: bool = False) -> DataResult:
        """
        根据查询条件获取这个数据表的所有的数据以列表的形式返回,支持按照某个字段排序
        默认是升序,如果最后一个参数是否反转,传入true是降序
        @param tableName:表名
        @param query:查询条件
        @param projection:过滤条件,过滤的字段不存在于结果中
        @param sortName:排序的字段名称
        @param isReverse:是否反转
        @return:
        """
        result = DataResult()
        if isinstance(tableName, TableName):
            tableName = tableName.name
        try:
            if query is None:
                query = {}
            if projection is None:
                projection = {
                    "_id": 0,
                    "time": 0
                }
            if sortName == "":
                findData = list(self.paramsDb[tableName].find(query, projection))
            else:
                direction = -1 if isReverse else 1
                findData = list(self.paramsDb[tableName].find(query, projection)
                                .sort(sortName, direction))
            result.state = True
            result.data = findData
        except Exception as e:
            result.info = str(e)
            debug_produce(f"in DbHandler().get_data_list() error: {str(e)}")
            return result
        return result

    def get_data_one(self, tableName: Union[str, TableName], query: Dict = None,
                     projection: Dict = None, sortName: str = "", isReverse: bool = False) -> DataResult:
        """
        根据数据库获取一条数据,返回的是单条数据的字典格式,不是列表格式,如果数据为空,也返回False,表示
        无数据
        @param tableName:表名
        @param query: 查询条件
        @param projection: 过滤条件,不返回的字段
        @param sortName: 排序字段名称
        @param isReverse: 是否进行翻转,默认正序,是否逆序
        @return:
        """
        result = DataResult()
        if isinstance(tableName, TableName):
            tableName = tableName.name
        try:
            if projection is None:
                projection = {
                    "_id": 0,
                    "time": 0
                }
            if query is None:
                query = {}
            if sortName == "":
                findData = list(self.paramsDb[tableName].find(query, projection))
            else:
                direction = -1 if isReverse else 1
                findData = list(self.paramsDb[tableName].find(query, projection)
                                .sort(sortName, direction))
            if len(findData) == 0:
                result.info = f"表 {tableName} 根据查找条件 {query} 查找的数据为空"
            else:
                result.data = findData[0]
                result.state = True
        except Exception as e:
            result.info = str(e)
            debug_produce(f"in DbHandler().get_data_one() error: {str(e)}")
            return result
        return result

    def init_data(self, tableName: Union[str, TableName],
                  initData: Union[Dict, List[Dict]]) -> DataResult:
        """
        根据表名和数据初始化表,如果表不存在就创建,存在就忽略
        :param tableName:
        :param initData:
        :return:
        """

        result = DataResult()
        try:
            if tableName not in self.paramsDb.list_collection_names():
                return self.insert_data(tableName, initData)
        except Exception as e:
            errInfo = f"in  init_data() error: {e}"
            result.errInfo = errInfo
            return result

    def re_init_data(self, tableName: Union[str, TableName],
                     initData: Union[Dict, List[Dict]]) -> DataResult:
        """
        重新初始化数据表,删除旧表内容,根据initData重新创建新的表
        :param tableName:
        :param initData:
        :return:
        """
        result = DataResult()
        try:
            self.drop_table(tableName)
            return self.init_data(tableName, initData)
        except Exception as e:
            errInfo = f"in  re_init_data() error: {e}"
            result.errInfo = errInfo
            return result


if __name__ == '__main__':
    db1 = DbHandler()
    db2 = DbHandler()
    db3 = DbHandler()
